【Flutter 专题】20 图解 ListView 下拉刷新与上拉加载 (三)【RefreshIndicator】

您所在的位置:网站首页 flutter 刷新listview 【Flutter 专题】20 图解 ListView 下拉刷新与上拉加载 (三)【RefreshIndicator】

【Flutter 专题】20 图解 ListView 下拉刷新与上拉加载 (三)【RefreshIndicator】

#【Flutter 专题】20 图解 ListView 下拉刷新与上拉加载 (三)【RefreshIndicator】| 来源: 网络整理| 查看: 265

      这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

      小菜前段时间整理了两种 ListView 的异步加载数据时,下拉刷新与上滑加载更多的方式,每种方式都有自己的优势,网上也有很多大神讲解过 ListView 数据流的种种处理方式,小菜根据实际遇到的情况整理一下尝试的第三种方案。

RefreshIndicator 下拉刷新

      Flutter 提供了自带刷新效果的 RefreshIndicator,这也是网上大神们用的最多的 Widget 之一,使用方式也很简单,RefreshIndicator 中提供了一个刷新的回调入口 onRefresh,仅需在该回调接口中处理数据请求即可,如下:

// 刷新时数据请求 Future _loadRefresh() async { await Future.delayed(Duration(seconds: 2), () { setState(() { dataItems.clear(); lastFileID = '0'; rowNumber = 0; _getNewsData(lastFileID, rowNumber); return null; }); }); } // 请求接口整合数据 _getNewsData(var lastID, var rowNum) async { await http .get( 'https://XXX.../getArticles?...&lastFileID=${lastID}&rowNumber=${rowNum}') .then((response) { if (response.statusCode == 200) { var jsonRes = json.decode(response.body); newsListBean = NewsListBean(jsonRes); if (lastID == '0' && rowNum == 0 && dataItems != null) { dataItems.clear(); } setState(() { if (newsListBean != null && newsListBean.list != null && newsListBean.list.length > 0) { for (int i = 0; i < newsListBean.list.length; i++) { dataItems.add(newsListBean.list[i]); } lastFileID = newsListBean.list[newsListBean.list.length - 1].fileID.toString(); rowNumber += newsListBean.list.length; } else {} }); } }); } // 绑定列表数据 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("第三种加载方式"), ), body: new RefreshIndicator( child: ListView.builder( itemCount: items.length, itemBuilder: buildListData(context, dataItems[index]) ), onRefresh: _loadRefresh, // 刷新回调 )); }

ScrollController 上滑动加载更多

      至此,列表的下拉刷新就完成了,接下来处理【上滑加载更多】,这时我们可以借助 ScrollController,用来监听列表是否滑动到底部,主要分两步:

初始化时添加监听事件,判断是否滑动到最底部; ListView 中添加监听方法。 ScrollController _scrollController = new ScrollController(); @override void initState() { super.initState(); _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { _getMoreData(); // 当滑到最底部时调用 } }); _getMoreData(); // 数据初始化 } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("第三种加载方式"), ), body: ListView.builder( itemCount: items.length, itemBuilder: buildListData(context, dataItems[index]), controller: _scrollController, )); }

      至此,列表的下拉刷新与上滑加载更多就基本完成了;接下来需要将两种合并使用,也很简单,如下:

body: new Padding( padding: EdgeInsets.all(2.0), child: RefreshIndicator( onRefresh: _loadRefresh, child: ListView.builder( itemCount: dataItems.length, physics: const AlwaysScrollableScrollPhysics(), itemBuilder: (context, index) { return buildListData(context, dataItems[index]); }, controller: _scrollController, )));

      Tips: 注意处理好数据接口请求内容。

小优化 优化一:【上滑加载更多】添加动画效果 添加一个加载更多的布局 Widget; 在 itemCount 中将 item 个数 +1; 添加监听判断,当滑到最后一个 item 时展示加载更多到布局 Widget。 body: new Padding( padding: EdgeInsets.all(2.0), child: RefreshIndicator( onRefresh: _loadRefresh, child: ListView.builder( itemCount: dataItems.length + 1, physics: const AlwaysScrollableScrollPhysics(), itemBuilder: (context, index) { if (index == dataItems.length) { return _buildProgressIndicator(); } else { return buildListData(context, dataItems[index]); } }, controller: _scrollController, )));

// 加载更多 Widget Widget _buildProgressIndicator() { return new Padding( padding: EdgeInsets.fromLTRB(0.0, 14.0, 0.0, 14.0), child: new Opacity( opacity: isShowLoading ? 1.0 : 0.0, child: new Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.center, children: [ new SpinKitChasingDots(color: Colors.blueAccent, size: 26.0), new Padding( padding: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0), child: new Text('正在加载中...')) ], ))); }

##### 优化二:第一次初始化加载数据时添加 **loading** 动画 ;;;;;;**RefreshIndicator** 中自带刷新的动画,所以小菜只是在第一次加载数据时添加一个 **loading** 动画,小菜只是填了一个小小的状态判断,如下包括异常情况下的失败页。

Widget childWidget() { Widget childWidget; if (newsListBean != null && (newsListBean.success != null && !newsListBean.success)) { isFirstLoading = false; childWidget = new Stack(children: [ new Padding( padding: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 100.0), child: new Center( child: Image.asset( 'images/icon_wrong.jpg', width: 120.0, height: 120.0, ))), new Padding( padding: new EdgeInsets.fromLTRB(0.0, 100.0, 0.0, 0.0), child: new Center( child: new Text( '抱歉!暂无内容哦~', style: new TextStyle(fontSize: 18.0, color: Colors.blue), ))) ]); } else if (dataItems != null && dataItems.length != 0) { isFirstLoading = false; childWidget = new Padding( padding: EdgeInsets.all(2.0), child: RefreshIndicator( onRefresh: _loadRefresh, child: ListView.builder( itemCount: dataItems.length + 1, physics: const AlwaysScrollableScrollPhysics(), itemBuilder: (context, index) { if (index == dataItems.length) { return _buildProgressIndicator(); } else { return buildListData(context, dataItems[index]); } }, controller: _scrollController, ))); } else { if (isFirstLoading) { // 只有在第一次加载数据时才会展示自定义 loading childWidget = new Center( child: new Card( child: new Stack(children: [ new Padding( padding: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 35.0), child: new Center( child: SpinKitFadingCircle( color: Colors.blueAccent, size: 30.0, ))), new Padding( padding: new EdgeInsets.fromLTRB(0.0, 35.0, 0.0, 0.0), child: new Center( child: new Text('正在加载中,莫着急哦~'), )) ])), ); } else {} } return childWidget; }

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e8b1e5bd1bd84c9ba8363e4b536e44f6~tplv-k3u1fbpfcp-zoom-1.image) ##### 优化三:借助 **Future.delayed()** 进行延迟加载,使数据请求衔接性更好。

_getMoreData() async { if (!isShowLoading) { setState(() { isShowLoading = true; }); await Future.delayed(Duration(seconds: 2), () { setState(() { _getNewsData(lastFileID, rowNumber); isShowLoading = false; return null; }); }); } }

*** ;;;;;;小菜刚接触 **Flutter** 时间不长,还有很多不清楚和不理解的地方,如果有不对的地方还希望多多指教。 > 来源:阿策小和尚


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3